package com.king.platform.utils.json;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.DefaultDeserializationContext;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
import com.fasterxml.jackson.databind.type.SimpleType;

import java.io.*;
import java.lang.annotation.Annotation;

public class CustomObjectMapper extends ObjectMapper {
  protected static final JavaType JSON_NODE_TYPE = SimpleType.constructUnsafe(JsonNode.class);

  public CustomObjectMapper() {
  }

  public CustomObjectMapper(JsonFactory jf, DefaultSerializerProvider sp, DefaultDeserializationContext dc) {
    super(jf, sp, dc);
  }

  public CustomObjectMapper(JsonFactory jf) {
    super(jf);
  }

  public CustomObjectMapper(ObjectMapper src) {
    super(src);
  }

  protected void autoconfigureFeatures(Object value) {
    if (value != null) {
      JavaType javaType = this._typeFactory.constructType(value.getClass());
      this.autoconfigureFeatures(javaType);
    }

  }

  protected void autoconfigureFeatures(JavaType javaType) {
    Annotation jsonRootNameAnnotation = javaType.getRawClass().getAnnotation(JsonRootName.class);
    boolean enabled = jsonRootNameAnnotation != null;
    this.configure(SerializationFeature.WRAP_ROOT_VALUE, enabled);
    this.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, enabled);
  }

  protected void autoconfigureFeatures(DeserializationConfig config, JavaType javaType) {
    Annotation jsonRootNameAnnotation = javaType.getRawClass().getAnnotation(JsonRootName.class);
    boolean enabled = jsonRootNameAnnotation != null;
    if (enabled) {
      config.with(DeserializationFeature.UNWRAP_ROOT_VALUE);
    } else {
      config.without(DeserializationFeature.UNWRAP_ROOT_VALUE);
    }

  }

  protected void autoconfigureFeatures(SerializationConfig config, JavaType javaType) {
    Annotation jsonRootNameAnnotation = javaType.getRawClass().getAnnotation(JsonRootName.class);
    boolean enabled = jsonRootNameAnnotation != null;
    if (enabled) {
      config.with(SerializationFeature.WRAP_ROOT_VALUE);
    } else {
      config.without(SerializationFeature.WRAP_ROOT_VALUE);
    }

  }

  protected ObjectReader _newReader(DeserializationConfig config, JavaType valueType, Object valueToUpdate, FormatSchema schema, InjectableValues injectableValues) {
    this.autoconfigureFeatures(config, valueType);
    return super._newReader(config, valueType, valueToUpdate, schema, injectableValues);
  }

  protected ObjectWriter _newWriter(SerializationConfig config, JavaType rootType, PrettyPrinter pp) {
    this.autoconfigureFeatures(config, rootType);
    return super._newWriter(config, rootType, pp);
  }

  protected JsonNode _readTreeAndClose(JsonParser p0) throws IOException {
    this.autoconfigureFeatures(JSON_NODE_TYPE);
    return super._readTreeAndClose(p0);
  }

  protected Object _readValue(DeserializationConfig cfg, JsonParser p, JavaType valueType) throws IOException {
    this.autoconfigureFeatures(cfg, valueType);
    return super._readValue(cfg, p, valueType);
  }

  public <T> MappingIterator<T> readValues(JsonParser p, JavaType valueType) throws IOException, JsonProcessingException {
    this.autoconfigureFeatures(valueType);
    return super.readValues(p, valueType);
  }

  protected Object _readMapAndClose(JsonParser p0, JavaType valueType) throws IOException {
    this.autoconfigureFeatures(valueType);
    return super._readMapAndClose(p0, valueType);
  }

  public void writeValue(JsonGenerator g, Object value) throws IOException, JsonGenerationException, JsonMappingException {
    this.autoconfigureFeatures(value);
    super.writeValue(g, value);
  }

  public void writeTree(JsonGenerator jgen, TreeNode rootNode) throws IOException, JsonProcessingException {
    this.autoconfigureFeatures((Object) rootNode);
    super.writeTree(jgen, rootNode);
  }

  public void writeTree(JsonGenerator jgen, JsonNode rootNode) throws IOException, JsonProcessingException {
    this.autoconfigureFeatures((Object) rootNode);
    super.writeTree(jgen, rootNode);
  }

  public void writeValue(File resultFile, Object value) throws IOException, JsonGenerationException, JsonMappingException {
    this.autoconfigureFeatures(value);
    super.writeValue(resultFile, value);
  }

  public void writeValue(OutputStream out, Object value) throws IOException, JsonGenerationException, JsonMappingException {
    this.autoconfigureFeatures(value);
    super.writeValue(out, value);
  }

  public void writeValue(DataOutput out, Object value) throws IOException {
    this.autoconfigureFeatures(value);
    super.writeValue(out, value);
  }

  public void writeValue(Writer w, Object value) throws IOException, JsonGenerationException, JsonMappingException {
    this.autoconfigureFeatures(value);
    super.writeValue(w, value);
  }

  public String writeValueAsString(Object value) throws JsonProcessingException {
    this.autoconfigureFeatures(value);
    return super.writeValueAsString(value);
  }

  public byte[] writeValueAsBytes(Object value) throws JsonProcessingException {
    this.autoconfigureFeatures(value);
    return super.writeValueAsBytes(value);
  }
}
